#include <cmath>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#define CAMERA_SPD 2.5f

class Camera {
private:
    float screenWidth;
    float screenHeight;

    glm::vec3 pos;
    glm::vec3 front;
    glm::vec3 up;

    float yaw;
    float pitch;
    float fov;
public:
    Camera(float w, float h, glm::vec3 pos, glm::vec3 up);
    glm::mat4 GetViewMatrix();
    glm::mat4 GetPerspective();
    glm::vec3 GetCamPos();
    glm::vec3 GetFront();

    void MoveForward(float deltaTs);
    void MoveBackward(float deltaTs);
    void StrideLeft(float deltaTs);
    void StrideRight(float deltaTs);

    void DeltaPitch(float val);
    void DeltaYaw(float val);
    void DeltaFOV(float val);
};

Camera::Camera(float w, float h, glm::vec3 pos, glm::vec3 up)
{
    screenWidth = w;
    screenHeight = h;

    yaw = -90.0f;
    pitch = 0.0f;
    fov = 45;

    this->pos = pos;
    this->up = up;
}

glm::mat4 Camera::GetViewMatrix()
{
    this->front = glm::vec3(
        cos(glm::radians(yaw)) * cos(glm::radians(pitch)),
        sin(glm::radians(pitch)),
        sin(glm::radians(yaw)) * cos(glm::radians(pitch))
    );
    return glm::lookAt(pos, pos + front, up);
}

glm::mat4 Camera::GetPerspective()
{
    return glm::perspective(glm::radians(fov), screenWidth / screenHeight, 0.1f, 100.0f);
}

glm::vec3 Camera::GetCamPos()
{
    return pos;
}

glm::vec3 Camera::GetFront()
{
    return front;
}

void Camera::MoveForward(float deltaTs)
{
    pos += front * deltaTs * CAMERA_SPD;
}

void Camera::MoveBackward(float deltaTs)
{
    pos -= front * deltaTs * CAMERA_SPD;
}

void Camera::StrideRight(float deltaTs)
{
    pos += glm::normalize(glm::cross(front, up)) * deltaTs * CAMERA_SPD;
}

void Camera::StrideLeft(float deltaTs)
{
    pos -= glm::normalize(glm::cross(front, up)) * deltaTs * CAMERA_SPD;
}

void Camera::DeltaPitch(float val)
{
    pitch = std::fmax(-89.0f, std::fmin(pitch + val, 89.0f));
}

void Camera::DeltaYaw(float val)
{
    yaw += val;
}

void Camera::DeltaFOV(float val)
{
   fov = std::fmax(1.0f, std::fmin( fov - val, 90.0f));
}

